﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace helicopterFight
{
    public enum type
    {
        MoveHelicopter = 0,
        AddRocket = 1,
        Hit = 2,
        Explode = 3,
        Score = 4,
        Respawn = 5,
        Fill = 6,
        Dispose = 7,
        Delete = 8
    }

    public class Network
    {
        private Game1 game;
        private NetworkSession networkSession;
        private HelicopterObject localPlayerHelicopter;
        private TestState gameState;

        PacketWriter packetWriter = new PacketWriter();
        PacketReader packetReader = new PacketReader();

        public Network(Game1 game, TestState gameState, HelicopterObject localPlayerHelicopter)
        {
            this.game = game;
            this.localPlayerHelicopter = localPlayerHelicopter;
            this.gameState = gameState;
        }

        /// <summary>
        /// Starts hosting a new network session.
        /// </summary>
        public void CreateSession()
        {
            Game1.konsola.addMessage("Creating session...");

            try
            {
                networkSession = NetworkSession.Create(NetworkSessionType.SystemLink,
                                                       1, 4);
                //Game1.konsola.addMessage("JES!");
                networkSession.GamerJoined += GamerJoinedEventHandler;
                networkSession.SessionEnded += SessionEndedEventHandler;
                networkSession.GamerLeft +=
                        new EventHandler<GamerLeftEventArgs>(session_GamerLeft);
            }
            catch (Exception e)
            {
                string errorMessage = e.Message;
                //Game1.konsola.addMessage(errorMessage);
            }
        }

        public void JoinSession()
        {
            Game1.konsola.addMessage("Joining session...");

            try
            {
                using (AvailableNetworkSessionCollection availableSessions =
                            NetworkSession.Find(NetworkSessionType.SystemLink,
                                                2, null))
                {
                    if (availableSessions.Count == 0)
                    {
                        CreateSession();
                        return;
                    }
                    networkSession = NetworkSession.Join(availableSessions[0]);
                    
                    networkSession.GamerJoined += GamerJoinedEventHandler;
                    networkSession.SessionEnded += SessionEndedEventHandler;
                    networkSession.GamerLeft += 
                        new EventHandler<GamerLeftEventArgs>(session_GamerLeft);
                }
            }
            catch (Exception e)
            {
                string errorMessage = e.Message;
                Game1.konsola.addMessage(errorMessage);
            }
        }
        void session_GamerLeft(object sender, GamerLeftEventArgs e)
        {
            Player player = e.Gamer.Tag as Player;
            for (int i = 0; i < gameState.obiekty.Count; i++)
            {
                if (player.helicopter == (gameState.obiekty[i] as HelicopterObject))
                {
                    gameState.obiekty.RemoveAt(i);
                    break;
                }
            }
            if (networkSession.IsHost)
                sendStats();
        }


        void GamerJoinedEventHandler(object sender, GamerJoinedEventArgs e)
        {
            int gamerIndex = networkSession.AllGamers.IndexOf(e.Gamer);
            Game1.konsola.addMessage("gamer joined " + gamerIndex);

            HelicopterObject helikopter1;

            if (e.Gamer.IsLocal)
            {
                helikopter1 = localPlayerHelicopter;
                helikopter1.name = e.Gamer.Gamertag;
                helikopter1.playerId = e.Gamer.Id;
            }
            else
            {
                helikopter1 = new HelicopterObject();
                Model helic = game.Content.Load<Model>("models/helicopter");
                helikopter1.model = helic;
                helikopter1.scale = 0.02f;
                helikopter1.position.X += 2100;
                helikopter1.position.Z -= 2100;
                helikopter1.position.Y += 800;
                helikopter1.name = e.Gamer.Gamertag;
                helikopter1.local = false;
                helikopter1.playerId = e.Gamer.Id;
                helikopter1.init(gameState);
                TestState.oppEmitter.Position = helikopter1.position;
                //if(!TestState.oppCue.IsPlaying) 
                //    TestState.oppCue.Play();
                gameState.obiekty.Add(helikopter1);
            }
            sendStats();
            e.Gamer.Tag = new Player(gamerIndex, helikopter1);
        }

        void SessionEndedEventHandler(object sender, NetworkSessionEndedEventArgs e)
        {
            string errorMessage = e.EndReason.ToString();
            Game1.konsola.addMessage(errorMessage);

            //sendBye();
            networkSession.Dispose();
            networkSession = null;            

            game.Exit();
        }

        public void sendRespawn(HelicopterObject ho)
        {
            if (!networkSession.IsHost)
            {   
                return;
            }

            foreach (NetworkGamer clientGamer in networkSession.AllGamers)
            {
                Player player = clientGamer.Tag as Player;

                //player.Update();

                packetWriter.Write((int)type.Respawn);
                packetWriter.Write(ho.playerId);
                packetWriter.Write(ho.position);
            }

            LocalNetworkGamer server = (LocalNetworkGamer)networkSession.Host;

            server.SendData(packetWriter, SendDataOptions.InOrder);
        }

        public void hit(Missile missile, HelicopterObject heli)
        {
            if (! networkSession.IsHost) {
                return;
            }

            foreach (NetworkGamer clientGamer in networkSession.AllGamers)
            {
                Player player = clientGamer.Tag as Player;

                packetWriter.Write((int)type.Hit);
                packetWriter.Write(missile.Owner.playerId);
                packetWriter.Write(heli.playerId);
                packetWriter.Write(missile.position);
            }

            LocalNetworkGamer server = (LocalNetworkGamer)networkSession.Host;

            server.SendData(packetWriter, SendDataOptions.InOrder);
        }

        public void addMissile(Missile missile)
        {
            foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
            {
                if (!networkSession.IsHost)
                {
                    missile.Ignore = true;

                    Player localPlayer = gamer.Tag as Player;
                    packetWriter.Write((int)type.AddRocket);

                    packetWriter.Write(gamer.Id);
                    packetWriter.Write(missile.position);
                    packetWriter.Write(missile.rotation);
                    packetWriter.Write(missile.direction);

                    gamer.SendData(packetWriter,
                                   SendDataOptions.InOrder, networkSession.Host);
                }
                else
                {
                    missile.Ignore = false;

                    foreach (NetworkGamer clientGamer in networkSession.AllGamers)
                    {
                        Player player = clientGamer.Tag as Player;

                        //player.Update();

                        packetWriter.Write((int)type.AddRocket);
                        packetWriter.Write(gamer.Id);
                        packetWriter.Write(missile.position);
                        packetWriter.Write(missile.rotation);
                        packetWriter.Write(missile.direction);
                    }

                    LocalNetworkGamer server = (LocalNetworkGamer)networkSession.Host;

                    server.SendData(packetWriter, SendDataOptions.InOrder);
                }

                break; // FIXME
            }
        }


        public void UpdateNetworkSession()
        {
            foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
            {
                UpdateLocalGamer(gamer);
            }
            if (networkSession.IsHost)
            {
                UpdateServer();
            }

            networkSession.Update();

            if (networkSession == null)
                return;

            foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
            {
                if (gamer.IsHost)
                {
                    ServerReadInputFromClients(gamer);
                }
                else
                {
                    ClientReadGameStateFromServer(gamer);
                }
            }
        }
        void UpdateLocalGamer(LocalNetworkGamer gamer)
        {
            Player localPlayer = gamer.Tag as Player;

            if (!networkSession.IsHost)
            {
                packetWriter.Write((int)type.MoveHelicopter);
                packetWriter.Write(localPlayer.helicopter.position);
                packetWriter.Write(localPlayer.helicopter.rotation);
                packetWriter.Write((double)localPlayer.helicopter.akceleracjaSmigla);

                gamer.SendData(packetWriter,
                               SendDataOptions.InOrder, networkSession.Host);
            }
        }

        public void fillRockets(HelicopterObject pl)
        {
            if (!networkSession.IsHost)
            {
                return;
            }
            NetworkGamer clientGamer = networkSession.FindGamerById(pl.playerId);

            Player player = clientGamer.Tag as Player;

            //player.Update();
            packetWriter.Write((int)type.Fill);
            packetWriter.Write(pl.playerId);

            LocalNetworkGamer server = (LocalNetworkGamer)networkSession.Host;

            server.SendData(packetWriter, SendDataOptions.InOrder);
        }

        public void sendStats()
        {
            if (!networkSession.IsHost)
            {
                return;
            }

            String msg = game.testState.parseList();
            game.testState.stats = msg;

            foreach (NetworkGamer gamer in networkSession.AllGamers)
            {
                Player player = gamer.Tag as Player;
                if (player == null)
                    return;
                player.Update();
                packetWriter.Write((int)type.Score);
                packetWriter.Write(msg);
            }

            LocalNetworkGamer server = (LocalNetworkGamer)networkSession.Host;
            server.SendData(packetWriter, SendDataOptions.InOrder);

        }

        public void sendBye()
        {
            try
            {
                foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
                {
                    if (!networkSession.IsHost)
                    {
                        Player localPlayer = gamer.Tag as Player;
                        packetWriter.Write((int)type.Dispose);
                        packetWriter.Write(localPlayer.helicopter.playerId);
                        gamer.SendData(packetWriter,
                                       SendDataOptions.InOrder, networkSession.Host);
                    }
                    break;
                }
            }
            catch (Exception e)
            {
                game.Exit();
            }
        }

        public void sendDelete(byte playerId)
        {
            if (!networkSession.IsHost)
            {
                return;
            }

            foreach (NetworkGamer gamer in networkSession.AllGamers)
            {
                Player player = gamer.Tag as Player;
                if (player == null)
                    return;
                packetWriter.Write((int)type.Delete);
                packetWriter.Write(playerId);
            }

            LocalNetworkGamer server = (LocalNetworkGamer)networkSession.Host;
            server.SendData(packetWriter, SendDataOptions.InOrder);

        }

        void UpdateServer()
        {
            foreach (NetworkGamer gamer in networkSession.AllGamers)
            {
                Player player = gamer.Tag as Player;

                if (player == null)
                    return;
                player.Update();

                packetWriter.Write((int)type.MoveHelicopter);
                packetWriter.Write(gamer.Id);
                packetWriter.Write(player.helicopter.position);
                packetWriter.Write(player.helicopter.rotation);
                packetWriter.Write((double)player.helicopter.akceleracjaSmigla);
            }

            LocalNetworkGamer server = (LocalNetworkGamer)networkSession.Host;

            server.SendData(packetWriter, SendDataOptions.InOrder);
        }

        void ServerReadInputFromClients(LocalNetworkGamer gamer)
        {
            while (gamer.IsDataAvailable)
            {
                NetworkGamer sender;

                gamer.ReceiveData(packetReader, out sender);

                if (!sender.IsLocal)
                {
                    Player remotePlayer = sender.Tag as Player;

                    type packetType = (type)packetReader.ReadInt32();
                    switch (packetType)
                    {
                        case type.MoveHelicopter:
                            {
                                //remotePlayer.helicopter.position = packetReader.ReadVector3();
                                remotePlayer.helicopter.setPosition(packetReader.ReadVector3());
                                remotePlayer.helicopter.rotation = packetReader.ReadVector3();
                                remotePlayer.helicopter.akceleracjaSmigla = (float)packetReader.ReadDouble();
                            }
                            break;
                        case type.AddRocket:
                            {
                                byte gamerId = packetReader.ReadByte();
                                NetworkGamer remoteGamer = networkSession.FindGamerById(gamerId);
                                Player player = remoteGamer.Tag as Player;

                                Vector3 position = packetReader.ReadVector3();
                                Vector3 rotation = packetReader.ReadVector3();
                                Vector3 direction = packetReader.ReadVector3();

                                Missile missile = player.helicopter.launchMissile(null, 0);
                                //missile.position = position; <- nie
                                missile.setPosition(position);
                                missile.rotation = rotation;
                                missile.direction = direction;
                                missile.Ignore = false;

                                foreach (NetworkGamer clientGamer in networkSession.AllGamers)
                                {
                                    Player clientPlayer = clientGamer.Tag as Player;

                                    //player.Update();

                                    packetWriter.Write((int)type.AddRocket);
                                    packetWriter.Write(gamerId);
                                    packetWriter.Write(position);
                                    packetWriter.Write(rotation);
                                    packetWriter.Write(direction);
                                }

                                LocalNetworkGamer server = (LocalNetworkGamer)networkSession.Host;

                                server.SendData(packetWriter, SendDataOptions.InOrder);
                            }
                            break;
                        case type.Dispose:
                            {
                                byte gamerId = packetReader.ReadByte();
                                NetworkGamer remoteGamer = networkSession.FindGamerById(gamerId);
                                Game1.konsola.setTraceString("odczytalem opuscil",gamerId.ToString());
                                Player player = remoteGamer.Tag as Player;                                
                                for (int i = 0; i < gameState.obiekty.Count; i++){
                                    if (player.helicopter == gameState.obiekty[i] as HelicopterObject){
                                        gameState.obiekty.RemoveAt(i);
                                        break;
                                    }                                    
                                }
                                sendDelete(gamerId);                                
                            }
                            break;
                    }

                }
            }            
        }

        void ClientReadGameStateFromServer(LocalNetworkGamer gamer)
        {
            while (gamer.IsDataAvailable)
            {
                NetworkGamer sender;

                gamer.ReceiveData(packetReader, out sender);

                while (packetReader.Position < packetReader.Length)
                {
                    type packetType = (type)packetReader.ReadInt32();

                    switch (packetType)
                    {
                        case type.MoveHelicopter:
                            {
                                byte gamerId = packetReader.ReadByte();

                                Vector3 position = packetReader.ReadVector3();
                                Vector3 rotation = packetReader.ReadVector3();
                                float akceleracjaSmigla = (float)packetReader.ReadDouble();

                                NetworkGamer remoteGamer = networkSession.FindGamerById(gamerId);

                                if (remoteGamer == null)
                                    break;

                                if (remoteGamer.IsLocal)
                                    continue;

                                if (remoteGamer != null)
                                {
                                    Player player = remoteGamer.Tag as Player;

                                    //player.helicopter.position = position;
                                    player.helicopter.setPosition(position);
                                    TestState.oppEmitter.Position = player.helicopter.position;
                                    player.helicopter.rotation = rotation;
                                    player.helicopter.akceleracjaSmigla = akceleracjaSmigla;
                                }
                            }
                            break;
                        case type.AddRocket:
                            {
                                byte gamerId = packetReader.ReadByte();

                                NetworkGamer remoteGamer = networkSession.FindGamerById(gamerId);
                                Player player = remoteGamer.Tag as Player;

                                Vector3 position = packetReader.ReadVector3();
                                Vector3 rotation = packetReader.ReadVector3();
                                Vector3 direction = packetReader.ReadVector3();

                                if (remoteGamer.IsLocal)
                                    continue;

                                Missile missile = player.helicopter.launchMissile(null, 0);
                                //missile.position = position; <- NIE
                                missile.setPosition(position);
                                missile.rotation = rotation;
                                missile.direction = direction;
                                missile.Ignore = true;
                            }
                            break;
                        case type.Hit:
                            {
                                byte missileOwnerId = packetReader.ReadByte();
                                NetworkGamer missileOwner = networkSession.FindGamerById(missileOwnerId);
                                Player playerOwner = missileOwner.Tag as Player;

                                byte targetId = packetReader.ReadByte();
                                NetworkGamer missileTarget = networkSession.FindGamerById(targetId);
                                Player playerTarget = missileTarget.Tag as Player;

                                Vector3 hitPosition = packetReader.ReadVector3();


                                playerTarget.helicopter.IsAlive = false;
                                //missile.Active = false;
                                game.testState.createExplosion(playerTarget.helicopter.position);
                                game.testState.createExplosion(hitPosition);

                                //if (remoteGamer.IsLocal)
                                //    continue;
                                //packetWriter.Write((int)type.Hit);
                                //packetWriter.Write(missile.Owner.playerId);
                                //packetWriter.Write(heli.playerId);
                            }
                            break;
                        case type.Score:
                            {
                                String gracze = packetReader.ReadString();
                                game.testState.stats = gracze;
                            }
                            break;
                        case type.Respawn:
                            {
                                byte OwnerId = packetReader.ReadByte();
                                NetworkGamer owner = networkSession.FindGamerById(OwnerId);

                                Player player = owner.Tag as Player;
                                Vector3 respPos = packetReader.ReadVector3();
                                player.helicopter.setPosition(respPos);
                                player.helicopter.IsAlive = true;
                            }
                            break;
                        case type.Fill:
                            {
                                byte playerID = packetReader.ReadByte();
                                NetworkGamer owner = networkSession.FindGamerById(playerID);
                                Player player = owner.Tag as Player;
                                player.helicopter.resetMissiles();
                            }
                            break;
                        case type.Delete:
                            {
                                byte gamerId = packetReader.ReadByte();
                                NetworkGamer remoteGamer = networkSession.FindGamerById(gamerId);
                                Player player = remoteGamer.Tag as Player;
                                for (int i = 0; i < gameState.obiekty.Count; i++)
                                {
                                    if (player.helicopter == (gameState.obiekty[i] as HelicopterObject))
                                    {
                                        gameState.obiekty.RemoveAt(i);
                                        break;
                                    }
                                }
                            }
                            break;
                    }

                }
            }
        }

        public NetworkSession getSession()
        {
            return this.networkSession;
        }
    }
}
